'''
Created on Mar 6, 2010

@author: Nick Heiner
'''
from BaseHandler import BaseHandler
from models import InCart, Item
from Utils import render
from Checkout import CheckOut
from google.appengine.api import memcache
from google.appengine.api.users import User
import re

class Cart(BaseHandler):
    """ Displays and does some operations on the shopping cart """
    
    def validate_email(self, email):
        """ Taken from http://commandline.org.uk/python/email-syntax-check/ """
        
        email_regex = "(?:[a-z0-9!#$%&'*+/=?^_{|}~-]+(?:.[a-z0-9!#$%" + \
                    "&'*+/=?^_{|}~-]+)*|\"(?:" + \
                    "[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]" + \
                    "|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9]" + \
                    "(?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?" + \
                    "|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.)" + \
                    "{3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?" + \
                    "|[a-z0-9-]*[a-z0-9]:(?:" + \
                    "[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]"  + \
                    "|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"
        return re.compile(email_regex).match(email)
    

    def handleRemoving(self, toRemove):
        errors = self.validateToRemove(toRemove, self.user)
        if errors:
            self.add_message("No items removed from the cart.", self.MSG_WARNING)
            for error_msg in errors:
                self.add_message(error_msg, self.MSG_ERROR)
        
        else:
            self.remove(self.request.get_all("delete"), self.user)


    def handleCheckout(self):
        anon = None
        messages = []
        if not self.user:
            email = self.request.get("email")
            if self.validate_email(email):
                anon = User(email=email)
            else:
                self.add_message("Email specified was invalid: %s" % email, self.MSG_ERROR)
        if self.user:
            messages = CheckOut().checkout(self.user)
        else:
            if anon:
                messages = CheckOut().checkout(anon, self.anon_key)
        
        for msg in messages:
            self.add_message(msg, self.MSG_NOTICE)


    def getCartEntries(self):
        if self.user:
            cartEntries = InCart.gql("WHERE owner = :1", self.user).fetch(1000) # what limit?
        else:
            cartEntries = memcache.get(self.anon_key)
        return cartEntries


    def body(self):
        toRender = None

        toRemove = self.request.get_all("delete") 
        if toRemove:
            self.handleRemoving(toRemove)
                
        if self.request.get("checkout") == "true":
            self.handleCheckout()
            
        cartEntries = self.getCartEntries()

        if cartEntries:
            cartItems = []
            for cartRow in cartEntries:
                cartItems.append({'item': cartRow.item,
                                  'count': cartRow.quantity,
                                  'unit_cost': cartRow.item.price,
                                  'cost': cartRow.quantity * cartRow.item.price})
                summary_stats = [{'name': 'Total Cost', 'value': '$%d' % sum(item['cost'] for item in cartItems)},
                                 {'name': 'Total Quantity', 'value': sum(entry['count'] for entry in cartItems)}
                                 ]
            def toRender():
                render('Views/checkout_form.html', self, {'user': self.user})
                render('Views/cart.html', self, {'items': cartItems,
                                                 'headers': ['Name', 'Quantity', 'Unit Cost', 'Sum cost', 'Delete?']})
    
                render('Views/cart_summary_stats.html', self, {'stats': summary_stats})
            
        else:
            self.add_message("Your cart is empty.", self.MSG_NOTICE)
        
        self.header("Shopping Cart", self.CART)
        if toRender:
            toRender()
        self.footer()
    
    
    def validateToRemove(self, toRemove, user):
        """ Makes sure that the input for removing cart items is valid """
        
        errors = []
        if not user and not self.anon_key: # this should never actually happen, but just in case...
            errors.append("No user is currently logged in and no anonymous cart key is specified.")

        if toRemove:
            for CSIN in toRemove:
                try:
                    CSIN_int = int(CSIN)
                    item = Item.gql("WHERE CSIN = :1", CSIN_int).get()
                    if not item:
                        errors.append("CSIN %d does not refer to any item" % CSIN_int)
                    else:
                        if user:
                            cartEntry = InCart.gql("WHERE item = :1 AND owner = :2", item, user).get()
                        else:
                            entries = memcache.get(self.anon_key)
                            cartEntry = filter(lambda x: x.item.CSIN == item.CSIN, entries)
                            if len(cartEntry) > 1:
                                errors.append("Multiple items have the same CSIN!")
                            
                        if not cartEntry:
                            errors.append("User does not have any of item %d (%s) in the cart" % (item.CSIN, item.name))

                except ValueError:
                    errors.append("CSIN should be an int, but was given as something else: %s" % CSIN)
        else:
            errors.append("No items specified to be removed")
            
        return errors        
            
    
    def remove(self, toRemove, user):
        """ toRemove is a list of CSINs to remove from the cart """
        
        for CSIN in toRemove:
            CSIN = int(CSIN)
            item = Item.gql("WHERE CSIN = :1", CSIN).get()
            if self.user:
                cartEntry = InCart.gql("WHERE item = :1 AND owner = :2", item, user).get()
                cartEntry.delete()
            else:
                entries = memcache.get(self.anon_key)
                cartEntry = filter(lambda x: x.item.CSIN == item.CSIN, entries)[0]
                entries.remove(cartEntry)
                if not memcache.replace(self.anon_key, entries, self.ANON_CART_TIMEOUT):
                    self.add_message("memcache set failure", self.MSG_ERROR)
            